// ------------------------------------------------------------------------------
// 
// Copyright (c) 2006-2008 Swampware, Inc.
// 
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// 
// ------------------------------------------------------------------------------
using System;

namespace Water
{
	/// <summary>
	/// Summary description for Generator.
	/// </summary>
	public sealed class Generator
	{
		private Water.TextWriter _writer;

		public Generator(Water.TextWriter writer)
		{
			this._writer = writer;
		}

		public void Generate(object value)
		{
			#region Generate

			if(value == null)
			{
				this._writer.WriteLine("null");
			}
			else if(value is bool)
			{
				bool b = (bool)value;

				this._writer.WriteLine(b.ToString());
			}
			else if(value is byte)
			{
				byte n = (byte)value;

				this._writer.WriteLine(n.ToString());
			}
			else if(value is short)
			{
				short n = (short)value;

				this._writer.WriteLine(n.ToString());
			}
			else if(value is int)
			{
				int n = (int)value;

				this._writer.WriteLine(n.ToString());
			}
			else if(value is long)
			{
				long n = (long)value;

				this._writer.WriteLine(n.ToString());
			}
			else if(value is decimal)
			{
				decimal d = (decimal)value;

				this._writer.WriteLine(d.ToString());
			}
			else if(value is float)
			{
				float f = (float)value;

				this._writer.WriteLine(f.ToString());
			}
			else if(value is double)
			{
				double d = (double)value;

				this._writer.WriteLine(d.ToString());
			}
			else if (value is System.DateTime)
			{
				System.DateTime dt = (System.DateTime)value;

				this._writer.WriteLine(dt.ToString());
			}
			else if (value is Water.Identifier)
			{
				Water.Identifier identifier = (Water.Identifier)value;

				this._writer.WriteLine(identifier.Value);
			}
			else if(value is Water.Quote)
			{
				Water.Quote quote = (Water.Quote)value;

				this._writer.Write("\'");
				Generate(quote.Expression);
			}
			else if(value is Water.Comma)
			{
				Water.Comma comma = (Water.Comma)value;

				this._writer.Write(",");
				Generate(comma.Expression);
			}
			else if(value is byte[])
			{
				byte[] bytes = (byte[])value;

				this._writer.Write("[");
				this._writer.Write(System.Convert.ToBase64String(bytes));
				this._writer.WriteLine("]");
			}
			else if(value is string)
			{
				string s = (string)value;

				this._writer.WriteLine("\"" + Escape(s) + "\"");
			}
			else if (value is System.Enum)
			{
				System.Enum enum_ = (System.Enum)value;

				this._writer.WriteLine(enum_.ToString());
			}
			else if ((value is System.Collections.IDictionary))
			{
				System.Collections.IDictionary dictionary = (System.Collections.IDictionary)value;

				this._writer.WriteLine("{");
				//TODO if(this._newlineString.Length > 0)
				{
					this._writer.Indent();
				}

				bool first = true;
				foreach (object key in dictionary.Keys)
				{
					try
					{
						object dictionaryValue = dictionary[key];
						if (this._writer.NewlineString.Length == 0)
						{
							if (first)
							{
								first = false;
							}
							else
							{
								this._writer.Write(" ");
							}
						}

						this._writer.Write(key.ToString() + ": ");
						Generate(dictionaryValue);
					}
					catch (System.Exception exception)
					{
						string thisIsAHackToIgnoreCompilerWarning = exception.Message;
					}
				}

				//TODO if(this._newlineString.Length > 0)
				{
					this._writer.Unindent();
				}
				this._writer.WriteLine("}");
			}
			else if (IsList(value))
			{
				System.Collections.IEnumerable list = (System.Collections.IEnumerable)value;

				this._writer.WriteLine("(");
				//TODO if(this._newlineString.Length > 0)
				{
					this._writer.Indent();
				}

				bool first = true;
				foreach(object item in list)
				{
					if(this._writer.NewlineString.Length == 0)
					{
						if(first)
						{
							first = false;
						}
						else
						{
							this._writer.Write(" ");
						}
					}

					Generate(item);
				}

				//TODO if(this._newlineString.Length > 0)
				{
					this._writer.Unindent();
				}
				this._writer.WriteLine(")");
			}
			else
			{
				this._writer.WriteLine("{");
				//TODO if(this._newlineString.Length > 0)
				{
					this._writer.Indent();
				}

				bool first = true;

				System.Type type = value.GetType();
				foreach(System.Reflection.FieldInfo fieldInfo in type.GetFields())
				{
					if (!fieldInfo.IsStatic)
					{
						try
						{
							object fieldValue = fieldInfo.GetValue(value);
							if (this._writer.NewlineString.Length == 0)
							{
								if (first)
								{
									first = false;
								}
								else
								{
									this._writer.Write(" ");
								}
							}

							this._writer.Write(fieldInfo.Name + ": ");
							Generate(fieldValue);
						}
						catch (System.Exception exception)
						{
							string thisIsAHackToIgnoreCompilerWarning = exception.Message;
						}
					}
				}

				System.ComponentModel.PropertyDescriptorCollection propertyDescriptors = System.ComponentModel.TypeDescriptor.GetProperties(value);
				foreach(System.ComponentModel.PropertyDescriptor propertyDescriptor in propertyDescriptors)
				{
					try
					{
						object propertyValue = propertyDescriptor.GetValue(value);
						if(this._writer.NewlineString.Length == 0)
						{
							if(first)
							{
								first = false;
							}
							else
							{
								this._writer.Write(" ");
							}
						}

						this._writer.Write(propertyDescriptor.Name + ": ");
						Generate(propertyValue);
					}
					catch(System.Exception exception)
					{
						string thisIsAHackToIgnoreCompilerWarning = exception.Message;
					}
				}

				//TODO if(this._newlineString.Length > 0)
				{
					this._writer.Unindent();
				}
				this._writer.WriteLine("}");
			}

			this._writer.Flush();

			#endregion
		}

		public static bool IsDictionary(object o)
		{
			return ((o is System.Collections.IDictionary) || (o is Water.Dictionary));
		}

		public static bool IsList(object o)
		{
			return ((o is System.Collections.IEnumerable) && !(o is string));
		}

		public static bool IsString(object value)
		{
			return (value is string);
		}

		public static bool IsDecimal(object value)
		{
			return (value is decimal);
		}

		public static bool IsDouble(object value)
		{
			return (value is double);
		}

		public static bool IsInt(object value)
		{
			return (value is int);
		}

		public static bool IsAtom(object value)
		{
			if(value is bool)
			{
				return true;
			}
			else if(value is byte)
			{
				return true;
			}
			else if(value is short)
			{
				return true;
			}
			else if(value is int)
			{
				return true;
			}
			else if(value is long)
			{
				return true;
			}
			else if(value is decimal)
			{
				return true;
			}
			else if(value is float)
			{
				return true;
			}
			else if(value is double)
			{
				return true;
			}
			else if(value is string)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		public static string Escape(string input)
		{
			#region Escape

			System.IO.StringReader reader = new System.IO.StringReader(input);
			System.IO.StringWriter writer = new System.IO.StringWriter();

			int n;
			while ((n = reader.Read()) != -1)
			{
				char ch = (char)n;
				switch (ch)
				{
					case '\\' :
					case '\'' :
					case '\"' :
					case '\0' :
					case '\a' :
					case '\b' :
					case '\f' :
					case '\n' :
					case '\r' :
					case '\t' :
					case '\v' :
					{
						writer.Write('\\');
						writer.Write(ch);
						break;
					}
					default :
					{
						writer.Write(ch);
						break;
					}
				}
			}
			
			return writer.ToString();

			//System.Text.StringBuilder stringBuilder = new System.Text.StringBuilder(input);

			////backslash
			//stringBuilder.Replace("\\", "\\\\");

			////single quote
			//stringBuilder.Replace("\'", "\\\'");

			////double quote
			//stringBuilder.Replace("\"", "\\\"");

			////null
			//stringBuilder.Replace("\0", "\\0");

			////alert
			//stringBuilder.Replace("\a", "\\a");

			////backspace
			//stringBuilder.Replace("\b", "\\b");

			////form feed
			//stringBuilder.Replace("\f", "\\f");

			////newline
			//stringBuilder.Replace("\n", "\\n");

			////carriage return
			//stringBuilder.Replace("\r", "\\r");

			////tab
			//stringBuilder.Replace("\t", "\\t");

			////vertical tab
			//stringBuilder.Replace("\v", "\\v");

			//return stringBuilder.ToString();

			#endregion
		}

		public static string QuickGenerate(object item)
		{
			return QuickGenerate(item, "", "");
		}

		public static string QuickGenerate(object value, string indentationString, string newlineString)
		{
			System.IO.StringWriter writer = new System.IO.StringWriter();
			Water.TextWriter textWriter = new Water.TextWriter(writer, indentationString, newlineString);
			Water.Generator generator = new Water.Generator(textWriter);
			generator.Generate(value);
			writer.Flush();
			string s = writer.ToString();
			textWriter.Close();
			return s;
		}

	}
}
